/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Mar 4, 2005
 *
 */
package edu.mit.csail.relo.parts;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.PredicateUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.ui.PlatformUI;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.rio.RdfDocumentWriter;
import org.openrdf.sesame.sail.StatementIterator;
import org.openrdf.vocabulary.RDF;

import edu.mit.csail.relo.ReloCore;
import edu.mit.csail.relo.ReloPlugin;
import edu.mit.csail.relo.commands.AddNodeAndRelCmd;
import edu.mit.csail.relo.eclipse.gef.IncrementalGraphicalEditPart;
import edu.mit.csail.relo.graph.GraphLayoutManager;
import edu.mit.csail.relo.modelBridge.Artifact;
import edu.mit.csail.relo.modelBridge.DerivedArtifact;
import edu.mit.csail.relo.modelBridge.DirectedRel;
import edu.mit.csail.relo.modelBridge.JoinedRelType;
import edu.mit.csail.relo.store.ReloRdfRepository;
import edu.mit.csail.relo.store.StoreUnionRepository;
import edu.mit.csail.relo.store.StoreUtil;
import edu.mit.csail.relo.ui.ReloEditor;
import edu.mit.csail.relo.utils.LoadUtils;

/**
 * Place for functionality that is essentially an RDF Controller/EditPart
 * @author vineet
 *
 */
public abstract class ArtifactEditPart extends AbstractReloEditPart {
    static final Logger logger = ReloPlugin.getLogger(ArtifactEditPart.class);

    public Artifact getArtifact() {
        return (Artifact) getModel();
    }

    @Override
    protected void createEditPolicies() {
        super.createEditPolicies();
        installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new ArtifactRelModificationEditPolicy());
    }
	

    /* 
     * Selection level (default is inferred)
     * Indicates interest based on how node was created and if it was selected 
     * in the past, it should be seperated from CodeUnitEditPart eventually for
     * a more detailed model
     * 
     * The implementation is currently not completely consisted though it 
     * should be alright (for eg. the selection level is not modified on 
     * selection change). 
     * 
     */
    public static final int inferredSL = 0;
    public static final int queriedSL = 1;
    public static final int focusedSL = 2;
    private int currSL = inferredSL;
    private long selTS = -1;
    
    public int getSelectionLevel() {
        return currSL;
    }
    private long getSelectionLevelInternalTS() {
    	return selTS;
    }
    public long getSelectionLevelEffTS() {
    	if (true) throw new RuntimeException("this code has not been tested");
    	
    	long ts = getSelectionLevelInternalTS();
    	
    	List children = getChildren();
    	for (Object part : children) {
			if (part instanceof ArtifactEditPart) ts = Math.max(ts, ((ArtifactEditPart)part).getSelectionLevelInternalTS());
		}
    	
    	List<ConnectionEditPart> listParts = null;
    	
		listParts = this.getSourceConnectionsAsEP();
    	for (ConnectionEditPart connEP : listParts) {
    		EditPart part = connEP.getTarget();
			if (part instanceof ArtifactEditPart) ts = Math.max(ts, ((ArtifactEditPart)part).getSelectionLevelInternalTS());
		}
    	
		listParts = this.getTargetConnectionsAsEP();
    	for (ConnectionEditPart connEP : listParts) {
    		EditPart part = connEP.getSource();
			if (part instanceof ArtifactEditPart) ts = Math.max(ts, ((ArtifactEditPart)part).getSelectionLevelInternalTS());
		}
    	
    	return ts;
    }
    public void incrSelectionLevel(int sl) {
        currSL = Math.max(currSL, sl);
        selTS = System.currentTimeMillis();
    }

	public boolean isModelFocused() {
		//return currSL == focusedSL;
		return currSL > inferredSL;
	}

    
	
	/*
	 * Detail level functionality
	 */
	protected int currDL = getDefaultDL();
	
	public int getDetailLevel() {
	    return currDL;
	}


    public int getMinimalDL() {
		return 0;
	}

    public int getDefaultDL() {
		return getMinimalDL();
	}

    public int getMaximumDL() {
		return getDefaultDL()+1;
	}

	/**
     * Used for debugging purposes
	 * @param dl
	 * @return
	 */
	public String getDLStr(int dl) {
	    if (dl == getMinimalDL())
	        return "minimalDL";
	    else
	        return "unknownDL";
	}

	public void suggestDetailLevelIncrease() {
	    //logger.debug(this.getClass().getName() + ".suggestDetailLevelIncrease: " + getDLStr(currDL));
		if (currDL <= getMaximumDL()) {
			updateMembers(currDL+1);
		    currDL++;
		}
	}
	public void suggestDetailLevelDecrease() {
	    //logger.debug(this.getClass().getName() + ".suggestDetailLevelDecrease: " + getDLStr(currDL));
	    //ConsoleView.logCause(new Exception());
		if (currDL >= getMinimalDL()) {
			updateMembers(currDL-1);
		    currDL--;
		}
	}
    



	
	@Override
    protected IFigure createFigure() {
		//logger.debug("Creating figure for: " + this);

		if (isModelFocused()) {
		    IFigure fig = createFigure(null, getDefaultDL());
		    currDL = getDefaultDL();
			return fig;
		} else {
			if (getDefaultDL() - 1 < getMinimalDL()) {
			    IFigure fig = createFigure(null, getMinimalDL());
			    currDL = getMinimalDL();
				return fig;
			} else {
			    IFigure fig = createFigure(null, getDefaultDL() - 1);
			    currDL = getDefaultDL() - 1;
				return fig;
			}
		}
	}

	
	// called to update to new detail level
	protected abstract IFigure createFigure(IFigure curFig, int newDL);

	protected void updateMembers(int newDL) {
	    if (newDL > currDL && newDL > getMinimalDL())
	        realizeChildrenArtifacts(getArtifact().getChildrenArtifacts(getRepo()));
	    if (newDL < currDL && newDL < getMaximumDL())
	        removeChildrenArtifacts(getArtifact().getChildrenArtifacts(getRepo()));
	}


	/*
	 * other misc. functionality
	 *
	 */

    //TODO: review - why don't we just call createofFindInferredArtifact here (unify if needed)
	public void realizeChildrenArtifacts(List lst) {
		Iterator childrenIt = lst.iterator();
		ReloController rc = (ReloController) getRoot().getContents();
		while (childrenIt.hasNext()) {
			Artifact child = (Artifact) childrenIt.next();

			if (!rc.isArtifactVisible(child)) {
				ArtifactEditPart childAEP = (ArtifactEditPart) createChild(child);
				childAEP.currDL = childAEP.getMinimalDL();

				appendModelAndChild(childAEP.getModel(), childAEP);
			}
		}
		
	}
	
	public void removeChildrenArtifacts(Collection lst) {
	    //logger.debug("removeChildrenCUs: size: " + lst.size());
		Iterator childrenIt = lst.iterator();
		while (childrenIt.hasNext()) {
			Artifact child = (Artifact) childrenIt.next();
			AbstractGraphicalEditPart childEP = findEditPart(child);
			if (childEP != null) 
			    removeModelAndChild(childEP);
		}
		
	}


	// TODO: is this the right place for this method?
	@Override
    public boolean removeModelAndChild(EditPart child) {
	    // support the fact that children may not be directly under the parent
	    if (child.getParent() != this && child.getParent().getModel() instanceof DerivedArtifact)
	        return ((IncrementalGraphicalEditPart) child.getParent()).removeModelAndChild(child);
	    else
	        return super.removeModelAndChild(child);
	}

	
	/**
     * Add menu items to only singly selected AEP's
     * 
     * TODO: find the likely better/official way to do this
	 * @param menu
	 */
	public void buildContextMenu(IMenuManager menu) {
	}

    public Action getRelAction(final String text, final DirectedRel rel, final Predicate filter) {
        return new Action(text) {
            @Override
            public void run() {
                CompoundCommand actionCmd = new CompoundCommand();
                ArtifactEditPart.this.showAllDirectRelation(actionCmd, rel, filter);
                if (actionCmd.size() > 0) ArtifactEditPart.this.execute(actionCmd);
            }
        };
    }

    public Action getRelAction(final String text, final DirectedRel rel) {
        return getRelAction(text, rel, null);
    }



    
    public void buildMultipleSelectionContextMenu(IMenuManager menu) {
        IAction action = new Action("Autobrowse") {
            @SuppressWarnings("unchecked")
            @Override
            public void run() {
                final List<ArtifactEditPart> selEP = getViewer().getSelectedEditParts();
                CollectionUtils.filter(selEP, PredicateUtils.instanceofPredicate(ArtifactEditPart.class));
                if (selEP.size() < 2) return;

                //PlatformUI.getWorkbench().getProgressService().runInUI()
                Job autoBrowseJob = new Job("Browsing graph") {
                    @Override
                    public IStatus run(IProgressMonitor monitor) {
                        autoBrowse(selEP, monitor);
                        return Status.OK_STATUS;
                    }
                };
                //autoBrowseJob.setSystem(true);
                autoBrowseJob.setUser(true);
                autoBrowseJob.setPriority(Job.INTERACTIVE);
                autoBrowseJob.schedule();
            }
        };
        menu.appendToGroup("main", action);
    }

    // TODO: clean the threading/scheduling functionality here (may need UNDO support)
    private void autoBrowse(List<ArtifactEditPart> selEP, IProgressMonitor monitor) {
        for (ArtifactEditPart endEP : selEP) {
            for (ArtifactEditPart startEP : selEP) {
                // check start and end so that we call auto-browse only once
                if (endEP == startEP) break;

                autoBrowse(startEP.getArtifact(), endEP.getArtifact(), 5, monitor);
            }
        }
    }

    protected void autoBrowse(Artifact startArt, Artifact endArt, int maxDepth, IProgressMonitor monitor) {
        logger.info("autoBrowsing: " + startArt + " // " + endArt);
        /*
        Pseudocode:
        1] let ptr=end
        2] ptrM1 = ptr-1
        3] get rel: ptr-?->ptrM1
        4] draw ptr-[rel]->ptrM1
        5] repeat till ptr=start
      */
        // draw from end-1
        Artifact ptr = endArt;
        while (!ptr.equals(startArt)) {
            logger.info("autoBrowsing - ptr: " + ptr);
            Artifact ptrM1 = breadthFirstSearch(startArt, ptr, maxDepth, monitor);
            if (ptrM1==null) return;

            // draw links
            final Artifact currPtr = ptr;
            final Artifact currPtrM1 = ptrM1;
            PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
                public void run() {
                    try {
                        URI rel;
    
                        // forward
                        rel = getRepo().getStatement(currPtrM1.elementRes, (URI) null, currPtr.elementRes).getPredicate();
                        if (rel != null) logger.info(currPtrM1 + " --" + rel + "--> " + currPtr);
                        if (rel != null && !rel.equals(ReloCore.contains)) getRootController().addRel(currPtrM1, rel, currPtr);
    
                        // reverse
                        rel = getRepo().getStatement(currPtr.elementRes, (URI) null, currPtrM1.elementRes).getPredicate();
                        if (rel != null) logger.info(currPtr + " --" + rel + "--> " + currPtrM1);
                        if (rel != null && !rel.equals(ReloCore.contains)) getRootController().addRel(currPtr, rel, currPtrM1);
                    } catch (Exception e) {
                        logger.error("Unexpected error while creating drawing figures", e);
                    }
                }});
            
            ptr = ptrM1;
        }
    }
    

    /**
     * Does breadth first search and returns the Artifact corresponding to the 
     * shortest path (just before the end)
     * 
     * @param startArt
     * @param endArt
     * @return - Artifact corresponding to endArt-1
     */
    private Artifact breadthFirstSearch(Artifact startArt, Artifact endArt, int maxDepth, IProgressMonitor monitor) {
        /*
         * Pseudocode: 
         * 1] before loop, if source!=target put in queue, else return null 
         * 2] while queue !empty, i.e. the loop: 
         * 3] pop from queue, and get all connected, if !=target queue 
         * 4] else: return current, i.e. the step before the target
         */
        Set<URI> filteredPreds = new HashSet<URI>();
        filteredPreds.add(StoreUtil.createMemURI(RDF.TYPE));
        filteredPreds.add(ReloCore.contains);
        
        if (startArt.equals(endArt)) {
            logger.error("breadthFirstSearch should not be called with start==end", new Exception());
            return null;
        }

        // note: visible artifacts don't really matter for breadth-first search
        //  (and in reality for autobrowse)
        Set<Artifact> visitedArtifact = new HashSet<Artifact>(1000);
        
        List<Artifact> searchQueue = new ArrayList<Artifact> (100);
        searchQueue.add(startArt);
        
        // use null as a marker to indicate the completion of one level of depth
        searchQueue.add(null);
        
        int currDepth = 0;
        
        while (true) {
            Artifact searchNode = searchQueue.remove(0);
            if (searchNode == null) {
                currDepth++;
                if (currDepth >= maxDepth) {
                    logger.info("Graph not connected in depth: " + maxDepth);
                    return null;
                }
                if (searchQueue.isEmpty()) {
                    logger.info("Graph not connected");
                    return null;
                }
                monitor.worked(1);
                if (monitor.isCanceled()) return null;
                searchQueue.add(null);
                continue;
            }
            for (Artifact child : searchNode.listConnectedArtifacts(getRepo(), filteredPreds)) {
                if (child.equals(endArt)) return searchNode;
                if (visitedArtifact.contains(child)) continue;
                if (!getRootController().artInferCreatable(child)) continue;

                searchQueue.add(child);
                visitedArtifact.add(child);
            }
        }
        
    }


    
    
    
    
    public void showAllDirectRelation(CompoundCommand tgtCmd, final DirectedRel rel, Predicate filter) {
        try {
            ReloController rc = getRootController();
            for (final Artifact relCU : getArtifact().listArt(getRepo(), rel, filter)) {
                if (rc.findArtifactEditPart(relCU) != null) continue;
                if (!rc.artCreatable(relCU)) continue;

                tgtCmd.add(new AddNodeAndRelCmd(rc, ArtifactEditPart.this, rel.res, relCU, rel.isFwd));
            }
        } catch (Throwable t) {
            logger.error("Unexpected exception", t);
        }
    }
    

    
	/**
	 * Shows the given relationship if the source and target are already visible
	 * @param relationRes
	 */
    public void showIncludedRelationships(CompoundCommand tgtCmd, URI relationRes) {
        if (!isActive()) return;

        Artifact curArt = getArtifact();
        if  (curArt instanceof DerivedArtifact) return;

        //logBeg("showIncludedRelationships");

        ReloController rc = getRootController();
        for (Artifact fwdArt : curArt.listArt(getRepo(), DirectedRel.getFwd(relationRes))) {
            ArtifactEditPart fwdArtEP = (ArtifactEditPart) findEditPart(fwdArt);
            if (fwdArtEP != null) {
                //log("Creating rel: " + this + " --> " + fwdArtEP);
                rc.addRel(tgtCmd, this, relationRes, fwdArtEP);
            }
        }
        for (Artifact revArt : curArt.listArt(getRepo(), DirectedRel.getRev(relationRes))) {
            ArtifactEditPart revArtEP = (ArtifactEditPart) findEditPart(revArt);
            if (revArtEP != null) {
                //log("Creating rel: " + revArtEP + " --> " + this);
                rc.addRel(tgtCmd, revArtEP, relationRes, this);
            }
        }
        //logEnd();
    }
	
    
    /**
     * hides all relationships to/from this part or any of its children 
     */
    public void hideRelationships(CompoundCommand tgtCmd, ReloController rc) {
        //Artifact curArt = getArtifact();
        //logger.info("in hideRelationships");

    	// call for all of our children
        for (Object childObj : getChildren()) {
            if (childObj instanceof ArtifactEditPart)
                ((ArtifactEditPart) childObj).hideRelationships(tgtCmd, rc);
		}
        
        // make sure we don't remove the same relationship twice
        List<ConnectionEditPart> removed = new ArrayList<ConnectionEditPart>();

        for (ConnectionEditPart relEP : getSourceConnectionsAsEP()) {
            if (!(relEP instanceof AbstractReloRelationPart))
                continue;
            if(!removed.contains(relEP)) {
            	rc.hideRel(tgtCmd, (AbstractReloRelationPart) relEP);
            	removed.add(relEP);
            }
		}

        for (ConnectionEditPart relEP : getTargetConnectionsAsEP()) {
            if (!(relEP instanceof AbstractReloRelationPart))
                continue;
            if(!removed.contains(relEP)) {
            	rc.hideRel(tgtCmd, (AbstractReloRelationPart) relEP);
            	removed.add(relEP);
            }
        }

    }
	
	/**
	 * Ensures that all elements on a valid path (given by rel) are shown
	 * Note: src.rel.tgt && src!=tgt has to be true 
	 * @param rel - join defining valid paths
	 */
	public void showPathElements(JoinedRelType rel) {
	    Artifact curArt = getArtifact();

		if (!isActive()) {
		    log("not active");
		    return;
		}

		logBeg("showPathElements");

		ReloController rc = getRootController();
        for (ArtifactEditPart visEP : rc.getVisibleNonDerivedArtifactEditParts()) {
			Artifact visArt = visEP.getArtifact();
			if (curArt.equals(visArt)) continue;
			//ObjectPair examiningPair = new ObjectPair(this, visEP);

            for (List<Artifact> resultSetVar : rel.getPaths(getRepo(), curArt, visArt)) {
                for (Artifact resultArt : resultSetVar) {
                    rc.createOrFindInferredArtifactEditPart(resultArt);
                }
            }

			// flip order of pair (everything else is identical
            for (List<Artifact> resultSetVar : rel.getPaths(getRepo(), visArt, curArt)) {
                for (Artifact resultArt : resultSetVar) {
                    rc.createOrFindInferredArtifactEditPart(resultArt);
                }
            }
		}
		logEnd();
	}

    /**
     * hides given node, moves all children to parent, and returns the parent
     * @return my parent before hiding
     */
    public IncrementalGraphicalEditPart hideAEP() {
        ArtifactEditPart myCUEP = this;
        ReloController rc = myCUEP.getRootController();
        IncrementalGraphicalEditPart myParent = (IncrementalGraphicalEditPart) myCUEP.getParent();
        
        List<EditPart> myChildren = new ArrayList<EditPart>(myCUEP.getChildrenAsTypedList());
        for (EditPart childEP : myChildren) {
            IncrementalGraphicalEditPart.moveModelAndChild((AbstractGraphicalEditPart) childEP, myCUEP, myParent);
            // deal with Derived AEP (and therefore recursive)
            if (childEP instanceof ArtifactEditPart
                    && ((ArtifactEditPart) childEP).getArtifact() instanceof DerivedArtifact) {
                //logger.info("hiding DerivedArtifact: " + ((ArtifactEditPart) childEP).getArtifact() + " " + childEP + " " + childEP.getClass());
                ((ArtifactEditPart) childEP).hideAEP();
            }
        }
        myCUEP.getModelChildren().clear();
        myCUEP.refresh();
        rc.removeNode(myCUEP);
        
        return myParent;
    }

    /**
     * Asserts parenthood for this AEP
     */
    public void assertParenthood() {
        CompoundCommand cc = new CompoundCommand();
        assertParenthood(cc);
        cc.execute();
    }
    
    /**
     * Asserts parenthood for this AEP
     */
    public void assertParenthood(CompoundCommand tgtCmd) {
        final ArtifactEditPart thisAEP = this;
        Artifact thisArt = thisAEP.getArtifact();
        ReloController rc = getRootController();
        
        //logger.info("assertParenthood: " + thisAEP);
        
        for (Artifact child : thisArt.getChildrenArtifacts(thisAEP.getRepo())) {
            final ArtifactEditPart childEP = (ArtifactEditPart) rc.findEditPart(child);
            if (childEP == null) continue;

            final AbstractReloEditPart oldParentEP = (AbstractReloEditPart) childEP.getParent();
            
            Command reparentCmd = new Command("reparent node") {
                @Override
                public void execute() { 
                    IncrementalGraphicalEditPart.moveModelAndChild(childEP, oldParentEP, thisAEP);
                }
                @Override
                public void undo() { 
                    IncrementalGraphicalEditPart.moveModelAndChild(childEP, thisAEP, oldParentEP);
                }
            };

            if (oldParentEP instanceof ReloController) {
                // was at the top level before
                tgtCmd.add(reparentCmd);
                continue;
            }
            
            ArtifactEditPart oldParentAEP = (ArtifactEditPart) oldParentEP;

            // want to do: if (oldParentAEP == thisAEP) continue;
            //      need to do this otherwise we will have infinite loops 
            // we also need to take into account derived code units as well
            
            if ( oldParentAEP.getArtifact().getNonDerivedBaseArtifact().equals(thisArt.getNonDerivedBaseArtifact())) {
                continue;
            }

            tgtCmd.add(reparentCmd);
        }
    }
    
    
    public void realizeParent(CompoundCommand tgtCmd, final boolean inferring) {
        //logger.info("realizeParent: " + this);
        //logger.info(this.getClass().getName() + " .realizeParent() - parent: " + parent
        //        + " / findEditPart(parent): " + findEditPart(parent));
        
        if (getModel() instanceof DerivedArtifact) return;

        final Artifact parentArt = getArtifact().getParentArtifact(getBrowseModel().getRepo());
        if (parentArt == null) return;
        
        final ReloController rc = getRootController();

        ArtifactEditPart parentEP = (ArtifactEditPart) findEditPart(parentArt);
        if (parentEP != null) {
            parentEP.assertParenthood(tgtCmd);
            return;
        }

        if (!rc.artCreatable(parentArt)) return;                    // check if browse model will allow
        if (inferring && !rc.artInferCreatable(parentArt)) return;  // check if execpetion management will allow
        
        tgtCmd.add(new Command("create parent and assert parenthood") {
            CompoundCommand parentAssertionCommand = null;
            ArtifactEditPart parentEP = null;
            @Override
            public void execute() { 
                parentEP = rc.createOrFindArtifactEditPart(parentArt, inferring ? ArtifactEditPart.inferredSL : ArtifactEditPart.queriedSL);
                
                parentEP.getFigure().setBounds(ArtifactEditPart.this.getFigure().getBounds().getCopy());
                GraphLayoutManager glm = (GraphLayoutManager) rc.getFigure().getLayoutManager();
                if  (glm.isLayedOut(ArtifactEditPart.this)) {
                    glm.setLayedOut(parentEP);
                    if  (glm.isPartAnchored(ArtifactEditPart.this)) glm.anchorPart(parentEP);
                }
                parentAssertionCommand = new CompoundCommand();
                parentEP.assertParenthood(parentAssertionCommand);
                parentAssertionCommand.execute();
            }
            @Override
            public void undo() { 
                parentAssertionCommand.undo();
                rc.removeNode(parentEP);
            }
        });

        
        return;
    }

    public static final URI posX = ReloCore.createReloURI("core#posX");
    public static final URI posY = ReloCore.createReloURI("core#posY");

	
	protected Resource viewRes = null;
    public Resource getViewRes() {
        if (viewRes==null) viewRes = getRepo().createBNode();
        return viewRes;
    }
    public void setViewRes(Resource viewRes) {
        this.viewRes = viewRes;
    }
	
    public static void writeRDF(ArtifactEditPart aep, RdfDocumentWriter rdfWriter) throws IOException {
    	rdfWriter.writeStatement(aep.getViewRes(), StoreUtil.createMemURI(RDF.TYPE), ReloCore.node);
        rdfWriter.writeStatement(aep.getViewRes(), ReloCore.model, aep.getArtifact().elementRes);

        Resource detailsNode =  StoreUtil.getDefaultStoreRepository().createBNode();
        rdfWriter.writeStatement(aep.getViewRes(), ReloCore.detailsNode, detailsNode);
        
        StatementIterator iter =  StoreUtil.getDefaultStoreRepository().getStatements(aep.getArtifact().elementRes, null, null);
        while(iter.hasNext()) {
        	Statement stmt = iter.next();
        	rdfWriter.writeStatement(detailsNode, stmt.getPredicate(), stmt.getObject());
        }
        aep.writeRDF(rdfWriter);
    }

    protected void writeRDF(RdfDocumentWriter rdfWriter) throws IOException {
	    final Rectangle rcBounds = getFigure().getBounds();
	    rdfWriter.writeStatement(viewRes, posX, StoreUtil.createMemLiteral(Integer.toString(rcBounds.x)));
	    rdfWriter.writeStatement(viewRes, posY, StoreUtil.createMemLiteral(Integer.toString(rcBounds.y)));
    }

    public static ArtifactEditPart readRDF(ReloController rc, ReloRdfRepository docRepo, Resource viewRes) {
        Resource modelRes = (Resource) docRepo.getStatement(viewRes, ReloCore.model, null).getObject();
        ReloRdfRepository bmRepo = rc.getBrowseModel().getRepo();
        
        // check and add decoration if the relo file is out-of-date
        if(LoadUtils.isOutdated(StoreUtil.getDefaultStoreRepository(), modelRes, rc.getReloEditorResource())) {
        	LoadUtils.addErrors(rc.getReloEditorResource(), modelRes, null);
        	ReloEditor editor = rc.getReloEditor();
        	if (editor != null) editor.addErrorDecoration();
        }
        
        if (bmRepo instanceof StoreUnionRepository) {
            readViewIntoCache(((StoreUnionRepository)bmRepo).getCacheRepo(), docRepo, viewRes); 
        }
        
        // if type is in document, use it to load class otherwise re-create artifact based on the browse model
        Resource docArtType = (Resource) docRepo.getStatement(modelRes, docRepo.rdfType, null).getObject();
        Artifact art = null;
        if (docArtType != null) {
            // use type in document, for loading model class from the main repo
            art = (Artifact) StoreUtil.loadClass(bmRepo, docArtType, ReloCore.model);
            art.init(modelRes);
        } else {
            art = new Artifact(modelRes);
        }
        //logger.info("Mapping: " + viewRes);

        //logger.debug("RC.AFA: " + viewRes + " --> " + modelRes);
        //logger.debug("prepping for: " + art.getClass() + " " + art);

        ArtifactEditPart aep = rc.createOrFindArtifactEditPart(art, ArtifactEditPart.focusedSL);
		aep.setViewRes(viewRes);

        //logger.debug("reading rdf for: " + art.getClass() + " " + art + " " + aep.getClass());
		aep.readRDF(docRepo);
		aep.refresh();

		return aep;
    }

    /**
     *  Put the file statements into a cached repo so that the diagram can be drawn the way it was saved
     */
    private static void readViewIntoCache(ReloRdfRepository cachedRepo, ReloRdfRepository docRepo, Resource viewRes) {
        Resource modelRes = (Resource) docRepo.getStatement(viewRes, ReloCore.model, null).getObject();

        // Part 1 of 2: ...when model is the value of the statement, i.e. contained by 
        // (relationships are taken care of in ArtifactRelEditPart)
        StatementIterator modelContainedByIter = docRepo.getStatements(null, ReloCore.contains, modelRes);
        cachedRepo.startTransaction();
        while(modelContainedByIter.hasNext()) {
        	Resource detailNode = modelContainedByIter.next().getSubject();
        	StatementIterator nodeIt = docRepo.getStatements(null, ReloCore.detailsNode, detailNode);
        	if(nodeIt.hasNext()) {
        		Resource node = nodeIt.next().getSubject();
        		Resource model = (Resource) docRepo.getStatement(node, ReloCore.model, null).getObject();
        		cachedRepo.addStatement(model, ReloCore.contains, modelRes);
        	}
        }
        cachedRepo.commitTransaction();
        
        // Part 2 of 2: ...when model is the subject of the statement, including: (i) contains, (ii) properties 
        // of the subject, and (iii) relationships from the subject (though these are not needed, but we just store them because we are lazy)
        Resource detailsNode = (Resource) docRepo.getStatement(viewRes, ReloCore.detailsNode, null).getObject();
        if (detailsNode == null) return;        // document does not have details in it
        StatementIterator modelDetailsIter = docRepo.getStatements(detailsNode, null, null);
        cachedRepo.startTransaction();
        while(modelDetailsIter.hasNext()) {
        	Statement stmt = modelDetailsIter.next();
        	cachedRepo.addStatement(modelRes, stmt.getPredicate(), stmt.getObject());
        }
        cachedRepo.commitTransaction();
    }
        
    protected void readRDF(ReloRdfRepository queryRepo) {
    	Value xpos = queryRepo.getStatement(viewRes, posX, null).getObject();
    	Value ypos = queryRepo.getStatement(viewRes, posY, null).getObject();
    	if (xpos != null && ypos != null) {
    	    int intXPos = Integer.parseInt(((Literal)xpos).getLabel());
    	    int intYPos = Integer.parseInt(((Literal)ypos).getLabel());
    	    getFigure().setLocation(new Point(intXPos, intYPos));
        }
        getRootController().getLayoutMgr().anchorPart(this);
    }
}
